搜索 K
Appearance
博客正在加载中...
Appearance
我们在实际开发中都会使用连接池,因为它可以减少我们获取连接所消耗的时间。如果忘了什么是连接池,可以复习:JDBC 连接池
连接池,本质上就是一个存储连接的容器,一个集合对象。该容器必须是线程安全的,不能两个线程拿到同一个连接;还必须实现队列的特性:先进先出。
使用连接池,可以省去一次查询中很耗时的部分:获取连接。
Mybatis 连接池提供了 3 种方式的配置:在主配置文件 SqlMapConfig.xml 中的 dataSource 标签,type 属性就是表示采用何种连接池方式,取值如下:
javax.sql.DataSource 规范中的连接池,Mybatis 中有针对规范的实现javax.sql.DataSource 接口,但是并没有使用连接池的思想,相当于不使用连接池。
可以看到 POOLED 是有获取连接,用完了会将连接返回给连接池的(Return connection to pool),而 UNPOOLED 则没有,每次都是创建一个连接,用完就销毁。
MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 PooledDataSource ,UnpooledDataSource 类来表示 POOLED、UNPOOLED 类型的数据源。
public class PooledDataSource implements DataSource
public class UnpooledDataSource implements DataSource{ 我们来看看 UnpooledDataSource 类获取 Connection 的源码,第 5 行就是使用原生的 JDBC 获取连接的方法,而 initializeDriver 方法则是注册驱动:也就是每次都注册驱动,获取连接,用完就关掉。
private Connection doGetConnection(Properties properties) throws SQLException {
this.initializeDriver();
Connection connection = DriverManager.getConnection(this.url, properties);
this.configureConnection(connection);
return connection;
}
private synchronized void initializeDriver() throws SQLException {
if (!registeredDrivers.containsKey(this.driver)) {
try {
Class driverType;
if (this.driverClassLoader != null) {
driverType = Class.forName(this.driver, true, this.driverClassLoader);
} else {
driverType = Resources.classForName(this.driver);
}
Driver driverInstance = (Driver)driverType.getDeclaredConstructor().newInstance();
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(this.driver, driverInstance);
} catch (Exception var3) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + var3);
}
}
}而在 PooledDataSource 中,getConnection 调用的是 popConnection 方法
public Connection getConnection() throws SQLException {
return this.popConnection(this.dataSource.getUsername(), this.dataSource.getPassword()).getProxyConnection();
} popConnection 部分源码如下:
private PooledConnection popConnection(String username, String password) throws SQLException {
//.......
while(conn == null) {
this.lock.lock();
try {
if (!this.state.idleConnections.isEmpty()) {
conn = (PooledConnection)this.state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode()
+ " from pool.");
}
} else if (this.state.activeConnections.size() < this.poolMaximumActiveConnections) {
conn = new PooledConnection(this.dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
else {
PooledConnection oldestActiveConnection = (PooledConnection)this.state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
// ........
}第 3 行,如果 connection 为 null,则锁住(lock.lock();,为了保证线程安全),然后第 7 行则判断是否有空余的连接(idle 有空闲的意思),有则取出第一个,idleConnections.remove(0)。在 Mybatis 中,共有两个连接池,一个是空闲的连接池,一个是活动的连接池。
如果没有在空闲的连接池中,没有空闲的连接了,则会判断 活动的连接池数量 是否小于设定的最大值(第 13 行),是则创建一个新的连接。
如果第 13 行判断不成立,说明活动连接池的连接数量,还未达到最大值,则获取最早创建的连接(第 19 行,oldestActiveConnection),并做一些设置,使其可以作为一个全新的 connection 使用。
这是 Mybatis 自己实现的连接池思想,不是用到了其他框架(例如 C3P0)。 而 idleConnections,就是一个数组而已:
public class PoolState {
protected final List<PooledConnection> idleConnections = new ArrayList();
}关于事务的概念,在学习数据库的时候大家应该都学过了,这里不再赘述。在 Mybatis 中,我们是通过 SqlSession 对象的 commit 方法和 rollback 方法实现事务的提交和回滚的,其底层也是调用 java.sql.Connection 的 commit 和 rollback 方法,Mybatis 部分源码如下:
package org.apache.ibatis.transaction.jdbc;
import java.sql.Connection;
public class JdbcTransaction implements Transaction {
protected Connection connection;
public void commit() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + this.connection + "]");
}
this.connection.commit();
}
}
public void rollback() throws SQLException {
if (this.connection != null && !this.connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection ["
+ this.connection + "]");
}
this.connection.rollback();
}
}
} 我们获取 SqlSession 对象的时候,默认是开启了事务的,也就是 Setting autocommit to false,不自动提交;我们可以在获取 SqlSession 的时候,传入一个参数 true,表明开启自动提交:
SqlSession session = factory.openSession(true); 之后就不用再调用 session.commit() 方法提交了。但我们一般不会设置自动提交,因为我们一般会多次与数据库交互,如果每次交互都提交,相当于没有事务控制。